package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.pojo.BeanDefinition;
import org.reflections.Reflections;
import org.reflections.scanners.FieldAnnotationsScanner;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.TypeAnnotationsScanner;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 注解Bean工厂<br>
 * 通过 @Service 来声明Bean
 * 通过 @AutoWired 来注入依赖的Bean
 * 通过 @Transactional 来声明方法事务支持
 */
public class AnnotationBeanFactory {

    // ---- 反射解析出来的信息 -----
    private Set<Class<?>> beanClasses = new HashSet<>();
    private Set<Field> autowiredFields = new HashSet<>();
    private Set<Method> transactionalMethods = new HashSet<>();

    /**
     * Bean定义集合
     */
    private Map<Class, BeanDefinition> beanDefinitions = new HashMap<>();

    // ---- 最终初始化好可用的Bean集合 ----
    private Map<String, Object> idObjectMap = new HashMap<>();
    private Map<Class, Object> classObjectMap = new HashMap<>();

    /**
     * 代理工厂
     */
    private ProxyFactory proxyFactory;

    public AnnotationBeanFactory(String basePackage) {
        proxyFactory = new ProxyFactory();
        startup(basePackage);
        proxyFactory.setTransactionManager(getBeanById("transactionManager"));
    }

    /**
     * 通过ID获取Bean
     *
     * @param id
     * @param <T>
     * @return
     */
    public <T> T getBeanById(String id) {
        return (T) idObjectMap.get(id);
    }

    /**
     * 通过类型获取Bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getBeanByType(Class clazz) {
        return (T) classObjectMap.get(clazz);
    }

    /**
     * 启动Bean工厂
     *
     * @param basePackage 包扫描路径
     */
    public void startup(String basePackage) {
        System.out.println(String.format("beanFactory startup start, basePackage=%s", basePackage));

        // 通过反射技术，扫描所有注解类、字段、方法
        scanAnnotations(basePackage);

        // 1.查找所有Bean定义，封装成BeanDefinition对象
        loadAllBeanDefinitions();

        // 2.初始化Bean
        System.out.println("----- create beans -----");
        beanDefinitions.values().forEach(b -> {
            doCreateBean(b);
        });

        System.out.println("beanFactory startup finish!");

        System.out.println("\n----- idObjectMap -----");
        idObjectMap.forEach((k, v) -> System.out.println(k + " - " + v.getClass()));
        System.out.println("\n----- classObjectMap -----");
        classObjectMap.forEach((k, v) -> System.out.println(k + " - " + v.getClass()));
    }

    /**
     * 扫描注解
     *
     * @param basePackage
     */
    private void scanAnnotations(String basePackage) {
        Reflections reflections = new Reflections(basePackage,
                TypeAnnotationsScanner.class, FieldAnnotationsScanner.class, MethodAnnotationsScanner.class);
        beanClasses = reflections.getTypesAnnotatedWith(Service.class);
        autowiredFields = reflections.getFieldsAnnotatedWith(Autowired.class);
        transactionalMethods = reflections.getMethodsAnnotatedWith(Transactional.class);
    }

    /**
     * 加载所有Bean定义
     */
    private void loadAllBeanDefinitions() {
        beanClasses.forEach(c -> {
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setBeanClass(c);

            // 1. 查找依赖
            beanDefinition.setDependencies(findDependencies(c));

            // 2. 检查是否需要事务代理
            beanDefinition.setTransactionProxy(checkNeedTransactionalProxy(c));

            // 3. 解析ID，默认为类名首字母小写
            Service service = c.getAnnotationsByType(Service.class)[0];
            beanDefinition.setId(!service.value().isEmpty() ? service.value() : getDefaultBeanId(getSuperClass(c)));

            // 4. 解析实现的接口
            beanDefinition.setInterfaces(c.getInterfaces());
            beanDefinition.setImplementInterface(c.getInterfaces() != null && c.getInterfaces().length > 0);

            // 5. 保存BeanDefinition集合
            beanDefinitions.put(getSuperClass(c), beanDefinition);
        });
    }

    /**
     * 创建Bean并初始化（注：不考虑循环依赖的情况）
     *
     * @param beanDefinition
     * @return
     */
    private Object doCreateBean(BeanDefinition beanDefinition) {
        Class beanClass = beanDefinition.getBeanClass();

        // 检查缓存是否已初始化过
        Object cachedObject = getBeanByType(getSuperClass(beanClass));
        if (cachedObject != null) {
            return cachedObject;
        }

        System.out.println(beanClass);
        Object instance = createInstance(beanDefinition);

        // 注入依赖
        beanDefinition.getDependencies().forEach(d -> {
            Class fieldType = getSuperClass(d.getType());
            Object bean = doCreateBean(beanDefinitions.get(fieldType));
            System.out.println("\t|---- " + fieldType);
            setFieldValue(d, instance, bean);
        });

        // 初始化好的Bean放入内存集合
        Object mayProxiedInstance = applyProxyAfterInit(beanDefinition, instance);
        idObjectMap.put(beanDefinition.getId(), mayProxiedInstance);
        classObjectMap.put(getSuperClass(beanClass), mayProxiedInstance);

        return mayProxiedInstance;
    }

    /**
     * 创建Bean实例，并且检查是否需要生成代理对象
     *
     * @param beanDefinition
     * @return
     */
    private Object createInstance(BeanDefinition beanDefinition) {
        try {
            Object instance = beanDefinition.getBeanClass().newInstance();
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 检查是否需要生成代理对象
     *
     * @param beanDefinition
     * @param instance
     * @return
     */
    private Object applyProxyAfterInit(BeanDefinition beanDefinition, Object instance) {
        if (beanDefinition.isTransactionProxy()) {
            // 默认规则：实现接口走JDK动态代理，否则走CGLIB代理
            return beanDefinition.isImplementInterface() ? proxyFactory.getJdkProxy(instance) : proxyFactory.getCglibProxy(instance);
        }
        return instance;
    }

    /**
     * 查询所有依赖
     *
     * @param clazz
     * @return
     */
    private Set<Field> findDependencies(Class clazz) {
        return autowiredFields.stream().filter(f -> f.getDeclaringClass().equals(clazz)).collect(Collectors.toSet());
    }

    /**
     * 检查是否需要事务代理
     *
     * @param clazz
     * @return
     */
    private boolean checkNeedTransactionalProxy(Class clazz) {
        return transactionalMethods.stream().anyMatch(m -> m.getDeclaringClass().equals(clazz));
    }

    /**
     * 获取默认的Bean ID
     *
     * @param clazz
     * @return
     */
    private String getDefaultBeanId(Class clazz) {
        String s = clazz.getSimpleName();
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder())
                    .append(Character.toLowerCase(s.charAt(0)))
                    .append(s.substring(1))
                    .toString();
        }
    }

    private void setFieldValue(Field field, Object bean, Object value) {
        try {
            field.setAccessible(true);
            field.set(bean, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private Class getSuperClass(Class clazz) {
        if (clazz.getInterfaces().length > 0) {
            return clazz.getInterfaces()[0];
        }
        return clazz;
    }

}
